-- Copyright (c) 2010, Pavel Kovar
-- All rights reserved.
--
---------------------------------------------------------------------------------------
-- This file is a part of the Witch Navigator project

-- Generation of the PCIe TPL packet
-- Implemented
--   * Single DW memory read completion
--   * 32 DWs memory write request
--   * DMA controller (simplified)
--   * 32 and 64 bits address

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

---- Uncomment the following library declaration if instantiating
---- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

ENTITY PCIeTX IS	
    PORT (
             signal clk : IN std_logic;
             signal rst_n : IN std_logic;

             signal trn_td : out std_logic_vector(31 downto 0);
             signal trn_tsof_n : out std_logic;
             signal trn_teof_n : out std_logic;
             signal trn_tsrc_rdy_n : out std_logic;
             signal trn_tsrc_dsc_n : out std_logic;
             signal trn_tdst_rdy_n : in std_logic;
             signal trn_tdst_dsc_n : in std_logic;      -- always 1 in PCIeIIP

             signal req_compl_i : in std_logic;         -- 1: after receiving MRd32/64
             signal compl_done_o : out std_logic;       -- 1: when coplD is done, which dma_wr_transfer = 0

             signal req_tc_i : in std_logic_vector(2 downto 0);
             signal req_td_i : in std_logic;
             signal req_ep_i : in std_logic;
             signal req_attr_i : in std_logic_vector(1 downto 0);
             signal req_len_i : in std_logic_vector(9 downto 0);
             signal req_rid_i : in std_logic_vector(15 downto 0);
             signal req_tag_i : in std_logic_vector(7 downto 0);
             signal req_be_i : in std_logic_vector(7 downto 0);
             signal req_addr_i : in std_logic_vector(19 downto 0);

             signal rd_addr_o : out std_logic_vector(19 downto 0); -- outmem_addr@cor_top
             signal rd_mem_ce_o : out std_logic;
             signal rd_data_i : in std_logic_vector(31 downto 0);

             signal completer_id_i : in std_logic_vector(15 downto 0);
             signal cfg_bus_mstr_enable_i : in std_logic;
             signal cfg_extended_tag_i : in std_logic;
             signal req_dma_w_i : in std_logic;
             signal dma_pc_addr_i : in std_logic_vector(31 downto 0);
             signal dma_done_o : out std_logic);    -- = 1 when write is done, which dma_wr_transfer = 1
end PCIeTX;  

architecture Behavioral of PCIeTX is
  -- Number of TLP pakts in DMA transfare 
    constant DMA_TLP_NO : integer := 255; 

  -- TLP Header format_type values
    constant PIO_32_CPLD_FMT_TYPE		   : std_logic_vector(6 downto 0) := "1001010";
    constant PIO_32_WR32_FMT_TYPE 		   : std_logic_vector(6 downto 0) := "1000000";
    constant PIO_32_RD64_FMT_TYPE  	       : std_logic_vector(6 downto 0) := "0100000";

  -- State
    constant PIO_32_TX_RST_STATE  		   : std_logic_vector(3 downto 0) := "0000";
    constant PIO_32_TX_CPL_CPLD_DW1	       : std_logic_vector(3 downto 0) := "0001";
    constant PIO_32_TX_CPL_CPLD_DW2	       : std_logic_vector(3 downto 0) := "0010";
    constant PIO_32_TX_CPLD_DW3			   : std_logic_vector(3 downto 0) := "0011";
    constant PIO_32_TX_MWR_DW1			   : std_logic_vector(3 downto 0) := "0100";
    constant PIO_32_TX_MWR_DW2			   : std_logic_vector(3 downto 0) := "0101";  
    constant PIO_32_TX_MWR_DATA			   : std_logic_vector(3 downto 0) := "0110";
    constant PIO_32_TX_NEXT_MWR_PAKET      : std_logic_vector(3 downto 0) := "0111";
    constant PIO_32_TX_NEXT_MWR_PAKET_DW0  : std_logic_vector(3 downto 0) := "1000";
    constant PIO_32_TX_WAIT_STATE          : std_logic_vector(3 downto 0) := "1001";

  -- Local registers
    signal rd_addr_o_pom    : std_logic_vector(19 downto 0);
    signal byte_count       : std_logic_vector(11 downto 0);
    signal lower_addr       : std_logic_vector(6 downto 0);     -- most bits depends on req_addr_i, LSB on req_be_i
    signal state            : std_logic_vector(3 downto 0);
    signal req_compl_q      : std_logic;                        -- transfer_start = 0, req_compl_i = 1
    signal dma_wr_transfer  : std_logic;                        -- 0: CplD, 1: Wr
    signal req_dma_w_q      : std_logic;                        -- 1: as the beginning pulse of dma write to memory, when dma_start = 0 and req_dma_w_i = 1
    signal dma_start        : std_logic;                        -- 1: as the beginning pulse of dma write to memory
    signal transfer_start   : std_logic;
    signal dma_pc_addr_r    : std_logic_vector(31 downto 0);

begin
    rd_addr_o <= rd_addr_o_pom;
    -- Calculate byte count based on byte enable
    byte_count <= "000000000100" when req_be_i(3)='1' and req_be_i(0)='1' else
                  "000000000011" when (req_be_i(3)='0' and req_be_i(2)='1' and req_be_i(0)='1') or (req_be_i(3)='1' and req_be_i(1)='1' and req_be_i(0)='0') else
                  "000000000010" when req_be_i="0011" or req_be_i="0110" or req_be_i="1100" else
                  "000000000001";

    -- Calculate lower address based on byte enable
    lower_addr(6 downto 2) <= req_addr_i(4 downto 0);
    lower_addr(1 downto 0) <= "00" when (req_be_i(3 downto 0) = "0000") or (req_be_i(0) = '1') else
    "01" when req_be_i(1 downto 0) = "10" else
    "10" when req_be_i(2 downto 0) = "100" else
    "11";

-- Transfer management
    process(clk)
    begin
        if clk'event and clk='1' then
            if (rst_n='0') then
                req_compl_q           <= '0';
                req_dma_w_q           <= '0';
            else
                if transfer_start='1' then
                    req_compl_q  		 <= '0';
                elsif req_compl_i='1' then
                    req_compl_q  		 <= '1';
                end if;
                if dma_start='1' then
                    req_dma_w_q			 <= '0';
                elsif req_dma_w_i='1' then
                    req_dma_w_q			 <= '1';
                end if;
            end if;
        end if;
    end process;
 
    rd_mem_ce_o <= not trn_tdst_rdy_n;

--  Generate completion with 1 DW payload 
--  Master 128 byte (32 DW) write request
--  DMA controller

    process(clk)
    begin
        if clk'event and clk='1' then
            if rst_n='0' then
                trn_tsof_n        <= '1';
                trn_teof_n        <= '1';
                trn_tsrc_rdy_n    <= '1';
                trn_tsrc_dsc_n    <= '1';
                trn_td            <= "00000000000000000000000000000000";

                compl_done_o      <= '0';
                dma_done_o			<= '0';
                dma_start			<= '0';
                transfer_start		<= '0';

                state             <= PIO_32_TX_RST_STATE;
            else

                case (state) is
                    when PIO_32_TX_RST_STATE =>
                        trn_tsrc_dsc_n     <= '1';
                        compl_done_o      <= '0';
                        dma_done_o			 <= '0';			 

                        if (req_compl_q='1' and trn_tdst_dsc_n='1') then
                        -- Begin a CplD TLP
                            trn_tsof_n       <= '0';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            trn_td(31)		  <= '0';
                            trn_td(30 downto 24) <= PIO_32_CPLD_FMT_TYPE;
                            trn_td(23)		  <= '0';
                            trn_td(22 downto 20) <= req_tc_i;
                            trn_td(19 downto 16) <= "0000";
                            trn_td(15) <= req_td_i;
                            trn_td(14) <= req_ep_i;
                            trn_td(13 downto 12) <= req_attr_i;
                            trn_td(11 downto 10) <= "00";
                            trn_td(9 downto 0) <= req_len_i;

                            dma_wr_transfer  <= '0';
                            state            <= PIO_32_TX_CPL_CPLD_DW1;
                            dma_start		  <= '0';
                            transfer_start	  <= '1';

                        elsif (req_dma_w_q='1') and (trn_tdst_dsc_n='1') and (cfg_bus_mstr_enable_i='1') then
                            trn_tsof_n       <= '0';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            trn_td(31)		  <= '0';
                            trn_td(30 downto 24) <=PIO_32_WR32_FMT_TYPE;
                            trn_td(23)		  <= '0';
                            trn_td(22 downto 20) <= "000"; --req_tc_i
                            trn_td(19 downto 16) <= "0000";
                            trn_td(15) <= '0'; --req_td_i
                            trn_td(14) <= '0'; --req_ep_i
                            trn_td(13 downto 12) <= "00"; --req_attr_i
                            trn_td(11 downto 10) <= "00";
                            trn_td(9 downto 0) <= "0000100000"; --32 DW - fixed write request length 

                            dma_pc_addr_r 	  <= dma_pc_addr_i;
                            rd_addr_o_pom 		  <="00000000000000000000";

                            dma_wr_transfer  <= '1';
                            state            <= PIO_32_TX_MWR_DW1;
                            dma_start		  <= '1';
                            transfer_start	  <= '0';	
                        else
                            trn_tsof_n       <= '1';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '1';
                            trn_td           <= "00000000000000000000000000000000";
                            state            <= PIO_32_TX_RST_STATE;
                            dma_start		  <= '0';
                            transfer_start	  <= '0';
                        end if;


                    when PIO_32_TX_CPL_CPLD_DW1 =>
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '0';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Output next DW of TLP
                            trn_tsof_n       <= '1';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            trn_td(31 downto 16) <= completer_id_i;
                            trn_td(15 downto 13) <= "000";
                            trn_td(12) <= '0';
                            trn_td(11 downto 0) <= byte_count;

                            state            <= PIO_32_TX_CPL_CPLD_DW2;
                            rd_addr_o_pom 		  <= req_addr_i;
                        else
                        -- Wait for core to accept previous DW
                            state            <= PIO_32_TX_CPL_CPLD_DW1;
                        end if;

                    when PIO_32_TX_CPL_CPLD_DW2 =>
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '0';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Output next DW of TLP
                            trn_tsof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            trn_td(31 downto 16)<= req_rid_i;
                            trn_td(15 downto 8)<= req_tag_i;
                            trn_td(7) <='0';
                            trn_td(6 downto 0)<=lower_addr;

                            trn_teof_n     <= '1';
                            state          <= PIO_32_TX_CPLD_DW3;
                        else
                        -- Wait for core to accept previous DW
                            state            <= PIO_32_TX_CPL_CPLD_DW2;
                        end if;

                -- datovy DW
                    when PIO_32_TX_CPLD_DW3 =>
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '1';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Output next DW of TLP
                            trn_tsof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            if req_be_i(0)='1' then trn_td(31 downto 24) <= rd_data_i(7 downto 0); else trn_td(31 downto 24) <="00000000"; end if;
                            if req_be_i(1)='1' then trn_td(23 downto 16) <= rd_data_i(15 downto 8); else trn_td(23 downto 16) <= "00000000"; end if;
                            if req_be_i(2)='1' then trn_td(15 downto 8) <= rd_data_i(23 downto 16); else trn_td(15 downto 8) <="00000000"; end if;
                            if req_be_i(3)='1' then trn_td(7 downto 0) <= rd_data_i(31 downto 24); else trn_td(7 downto 0) <= "00000000"; end if;
                            state            <= PIO_32_TX_WAIT_STATE; -- 1 DW of data only
                            trn_teof_n       <= '0';
                        else
                        -- Wait for core to accept previous DW
                            state            <= PIO_32_TX_CPLD_DW3;
                        end if;

                    when PIO_32_TX_MWR_DW1 =>	  
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '0';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Output next DW of TLP
                            trn_tsof_n       <= '1';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            trn_td(31 downto 16)<=completer_id_i;
                            trn_td(15 downto 8)<="00000000";--TAG
                            trn_td(7 downto 4)<="1111";--last DW BE
                            trn_td(3 downto 0)<="1111";--1. DW BE
                            state            <= PIO_32_TX_MWR_DW2;
                        else
                        -- Wait for core to accept previous DW
                            state            <= PIO_32_TX_MWR_DW1;
                        end if;

                    when PIO_32_TX_MWR_DW2 =>
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '0';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Output next DW of TLP
                            trn_tsof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            trn_td(31 downto 2) <= dma_pc_addr_r(31 downto 2);
                            trn_td(1 downto 0) <= "00";
                            rd_addr_o_pom 	    <= rd_addr_o_pom + 1;
                            state            <= PIO_32_TX_MWR_DATA;	--memory write
                            trn_teof_n       <= '1';
                        else
                        -- Wait for core to accept previous DW
                            state            <= PIO_32_TX_MWR_DW2;
                        end if;

                    when PIO_32_TX_MWR_DATA =>
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '0';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Output next DW of TLP
                            trn_tsof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';
                            trn_td(31 downto 24)<=rd_data_i(7 downto 0);
                            trn_td(23 downto 16)<=rd_data_i(15 downto 8);
                            trn_td(15 downto 8)<=rd_data_i(23 downto 16);
                            trn_td(7 downto 0)<=rd_data_i(31 downto 24);

                            if (rd_addr_o_pom(4 downto 0) = "00000") then -- konec paketu  
                                trn_teof_n     <= '0';
                                dma_pc_addr_r(31 downto 7)  <= dma_pc_addr_r(31 downto 7) + 1; -- PC adres incrementation of 128
                                if (rd_addr_o_pom(19 downto 5) = CONV_STD_LOGIC_VECTOR(DMA_TLP_NO, 15)) then -- Number of TPL pakets length 32DW
                                    state          <= PIO_32_TX_WAIT_STATE;
                                else
                                    state          <= PIO_32_TX_NEXT_MWR_PAKET;
                                end if;					  
                            else
                                rd_addr_o_pom 	     <= rd_addr_o_pom + 1;
                                state          <= PIO_32_TX_MWR_DATA;
                                trn_teof_n     <= '1';
                            end if;				  
                        else
                        -- Wait for core to accept previous DW
                            state            <= PIO_32_TX_MWR_DATA;
                        end if;

                    when PIO_32_TX_NEXT_MWR_PAKET =>
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '0';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Core has accepted final DW of TLP
                            trn_tsof_n       <= '1';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '1';
                            state            <= PIO_32_TX_NEXT_MWR_PAKET_DW0;
                        else
                            state            <= PIO_32_TX_NEXT_MWR_PAKET;
                        end if; 

                    when PIO_32_TX_NEXT_MWR_PAKET_DW0 =>
                        if (trn_tdst_dsc_n='1' and cfg_bus_mstr_enable_i='1') then
                            trn_tsof_n       <= '0';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '0';

                            trn_td(31)		  <= '0';
                            trn_td(30 downto 24) <=PIO_32_WR32_FMT_TYPE;
                            trn_td(23)		  <= '0';
                            trn_td(22 downto 20) <= "000"; --req_tc_i
                            trn_td(19 downto 16) <= "0000";
                            trn_td(15) <= '0'; --req_td_i
                            trn_td(14) <= '0'; --req_ep_i
                            trn_td(13 downto 12) <= "00"; --eq_attr_i
                            trn_td(11 downto 10) <= "00";
                            trn_td(9 downto 0) <= "0000100000";--req_len_i  32DW - fixed DMA packet length

                            state            <= PIO_32_TX_MWR_DW1;
                        else
                            trn_tsof_n       <= '1';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '1';
                            trn_td           <= "00000000000000000000000000000000";
                            state            <= PIO_32_TX_RST_STATE;
                            dma_start		  <= '0';
                            transfer_start	  <= '0';
                        end if;		  

                    when PIO_32_TX_WAIT_STATE =>
                        dma_start		     <= '0';
                        transfer_start	  <= '0';
                        if (trn_tdst_dsc_n='0') then
                        -- Core is aborting
                            trn_tsrc_dsc_n   <= '0';
                            state            <= PIO_32_TX_RST_STATE;
                        elsif (trn_tdst_rdy_n='0') then
                        -- Core has accepted DW of TLP
                            trn_tsof_n       <= '1';
                            trn_teof_n       <= '1';
                            trn_tsrc_rdy_n   <= '1';
                            if (dma_wr_transfer='1') then
                                dma_done_o     <='1';
                            else
                                compl_done_o   <= '1';
                            end if;				  
                            state            <= PIO_32_TX_RST_STATE;
                        else
                        -- Wait for core to accept previous DW
                            state            <= PIO_32_TX_WAIT_STATE;
                        end if;
                    when others =>
                        state            <= PIO_32_TX_RST_STATE;
                end case;
            end if;
        end if;	 
    end process;

end Behavioral;

